#include "W25Q32.h"
#include "string.h"

__WEAK void W25Q32_CS(uint8_t enable)
{
	
	return;
}


__WEAK uint8_t W25Q32_SendReadByte ( uint8_t send_byte ) 
{
	send_byte = send_byte;
	return 0;
}


__WEAK uint8_t W25Q32_SendBuffer(uint8_t *buf,uint8_t size)
{	
	return 0;
}

__WEAK uint8_t W25Q32_RecvBuffer(uint8_t *buf,uint8_t size)
{	
	return 0;
}


typedef __packed struct
{
	uint8_t Manufacturer;             /* Manufacturer ID */
	uint16_t id;	
} jedec_id_t;

/* Private define ------------------------------------------------------------*/

#define W25QXX_DEBUG
#ifdef W25QXX_DEBUG
#define w25qxx_debug(fmt, ...)  printf(fmt, ##__VA_ARGS__)
#else
#define w25qxx_debug(fmt, ...)
#endif

#define JEDEC_MANUFACTURER_ST       0x20
#define JEDEC_MANUFACTURER_MACRONIX 0xC2
#define JEDEC_MANUFACTURER_WINBOND  0xEF

/* JEDEC Device ID: Memory type and Capacity */
#define JEDEC_W25Q16_BV_CL_CV   (0x4015) /* W25Q16BV W25Q16CL W25Q16CV  */
#define JEDEC_W25Q16_DW         (0x6015) /* W25Q16DW  */
#define JEDEC_W25Q32_BV         (0x4016) /* W25Q32BV */
#define JEDEC_W25Q32_DW         (0x6016) /* W25Q32DW */
#define JEDEC_W25Q64_BV_CV      (0x4017) /* W25Q64BV W25Q64CV */
#define JEDEC_W25Q64_DW         (0x4017) /* W25Q64DW */
#define JEDEC_W25Q128_BV        (0x4018) /* W25Q128BV */


#define JEDEC_WRITE_ENABLE           0x06
#define JEDEC_WRITE_DISABLE          0x04
#define JEDEC_READ_STATUS            0x05
#define JEDEC_WRITE_STATUS           0x01
#define JEDEC_READ_DATA              0x03
#define JEDEC_FAST_READ              0x0b
#define JEDEC_DEVICE_ID              0x9F
#define JEDEC_PAGE_WRITE             0x02
#define JEDEC_SECTOR_ERASE           0x20
#define JEDEC_CHIP_ERASE  					 0xC7//0x60
#define JEDEC_32KB_ERASE 						 0x52
#define JEDEC_64KB_ERASE 						 0xD8


#define JEDEC_STATUS_BUSY            0x01
#define JEDEC_STATUS_WRITEPROTECT    0x02
#define JEDEC_STATUS_BP0             0x04
#define JEDEC_STATUS_BP1             0x08
#define JEDEC_STATUS_BP2             0x10
#define JEDEC_STATUS_TP              0x20
#define JEDEC_STATUS_SEC             0x40
#define JEDEC_STATUS_SRP0            0x80
#define DUMMY_BYTE     0xFF

//	W25Q32_SectorErase(0,1); //43ms
//	W25Q32_ChipErase(1);  	 //5710ms
//	W25Q32_32KBlockErase(0,1); //119ms
//	W25Q32_64KBlockErase(0,1);-  //177ms

static uint8_t W25Q32_WaitForEnd ( uint32_t );
static uint8_t W25Q32_ReadID ( jedec_id_t *id );
static void W25Q32_WriteEnable ( void );


jedec_id_t flash_id;
void W25Q32_Init ( void )
{
    W25Q32_CS(0);
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);
    /* Send "0xff " instruction */
    W25Q32_SendReadByte ( DUMMY_BYTE );
		W25Q32_CS(0);
    /* read flash id */
    W25Q32_ReadID ( &flash_id );

    if ( flash_id.Manufacturer == JEDEC_MANUFACTURER_WINBOND )
    {
        if ( flash_id.id == ( JEDEC_W25Q32_BV & 0xff ) )
        {
            w25qxx_debug ( "W25Q32_BV detection\r\n" );
        }
        else if ( flash_id.id == ( JEDEC_W25Q64_DW & 0xff ) )
        {
            w25qxx_debug ( "W25Q64_DW or W25Q64_BV or W25Q64_CV detection\r\n" );
        }
        else if ( flash_id.id == ( JEDEC_W25Q32_DW & 0xff ) )
        {
            w25qxx_debug ( "W25Q32_DW or W25Q32_BV detection\r\n" );
        }
        else if ( flash_id.id == ( JEDEC_W25Q16_DW & 0xff ) )
        {
            w25qxx_debug ( "W25Q16_DW or W25Q16_BV detection\r\n" );
        }
        else
        {
            w25qxx_debug ( "error flash capacity\r\n" );
        }
    }
    else
    {
        w25qxx_debug ( "Unknow Manufacturer ID!%02X\r\n", flash_id.id );
        return ;
    }
}
static void W25Q32_WriteEnable ( void )
{
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);
    /* Send Write Enable instruction */
    W25Q32_SendReadByte ( JEDEC_WRITE_ENABLE );
    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);
}
uint32_t erase_time = 0;

uint8_t W25Q32_SectorErase ( uint32_t address, uint8_t state )
{
		/* Wait the end of Flash writing */
    if( W25Q32_ReadStatus() & JEDEC_STATUS_BUSY ) //busy status
		{
			return 0xff;
		}
		W25Q32_WriteEnable();
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);
    /* Send Sector Erase instruction */
    W25Q32_SendReadByte ( JEDEC_SECTOR_ERASE );
    /* Send SectorAddr high nibble address byte */
    W25Q32_SendReadByte ( ( address & 0xFF0000 ) >> 16 );
    /* Send SectorAddr medium nibble address byte */
    W25Q32_SendReadByte ( ( address & 0xFF00 ) >> 8 );
    /* Send SectorAddr low nibble address byte */
    W25Q32_SendReadByte ( address & 0xFF );
    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);
    /* Wait the end of Flash writing */
		// uint32_t time = HAL_GetTick();
		// erase_time = 0;
    if ( state )
    {
        W25Q32_WaitForEnd(0);
    }
		// erase_time = HAL_GetTick() - time;
		return 0;
}

uint8_t W25Q32_ChipErase ( uint8_t state )
{
		/* Wait the end of Flash writing */
    if( W25Q32_ReadStatus() & JEDEC_STATUS_BUSY ) //busy status
		{
			return 0xff;
		}
		W25Q32_WriteEnable();
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);
    /* Send Sector Erase instruction */
    W25Q32_SendReadByte ( JEDEC_CHIP_ERASE );
    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);
    /* Wait the end of Flash writing */
		// uint32_t time = HAL_GetTick();
		// erase_time = 0;
    if ( state )
    {
        W25Q32_WaitForEnd(0);
    }
		// erase_time = HAL_GetTick() - time;
		return 0;
}



uint8_t W25Q32_32KBlockErase ( uint32_t address, uint8_t state )
{
		/* Wait the end of Flash writing */
    if( W25Q32_ReadStatus() & JEDEC_STATUS_BUSY ) //busy status
		{
			return 0xff;
		}
		W25Q32_WriteEnable();
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);
    /* Send Sector Erase instruction */
    W25Q32_SendReadByte ( JEDEC_32KB_ERASE );
    /* Send SectorAddr high nibble address byte */
    W25Q32_SendReadByte ( ( address & 0xFF0000 ) >> 16 );
    /* Send SectorAddr medium nibble address byte */
    W25Q32_SendReadByte ( ( address & 0xFF00 ) >> 8 );
    /* Send SectorAddr low nibble address byte */
    W25Q32_SendReadByte ( address & 0xFF );
    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);
    /* Wait the end of Flash writing */
		// uint32_t time = HAL_GetTick();
		// erase_time = 0;
    if ( state )
    {
        W25Q32_WaitForEnd(0);
    }
		// erase_time = HAL_GetTick() - time;
		return 0;
}

uint8_t W25Q32_64KBlockErase ( uint32_t address, uint8_t state )
{
		/* Wait the end of Flash writing */
    if( W25Q32_ReadStatus() & JEDEC_STATUS_BUSY ) //busy status
		{
			return 0xff;
		}
		W25Q32_WriteEnable();
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);
    /* Send Sector Erase instruction */
    W25Q32_SendReadByte ( JEDEC_64KB_ERASE );
    /* Send SectorAddr high nibble address byte */
    W25Q32_SendReadByte ( ( address & 0xFF0000 ) >> 16 );
    /* Send SectorAddr medium nibble address byte */
    W25Q32_SendReadByte ( ( address & 0xFF00 ) >> 8 );
    /* Send SectorAddr low nibble address byte */
    W25Q32_SendReadByte ( address & 0xFF );
    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);
    /* Wait the end of Flash writing */
		// uint32_t time = HAL_GetTick();
		// erase_time = 0;
    if ( state )
    {
        W25Q32_WaitForEnd(0);
    }
		// erase_time = HAL_GetTick() - time;
		return 0;
}

/**
  * @brief  Writes more than one byte to the FLASH with a single WRITE
  *         cycle(Page WRITE sequence). The number of byte can't exceed
  *         the FLASH page size.
  * @param pBuffer : pointer to the buffer  containing the data to be
  *                  written to the FLASH.
  * @param WriteAddr : FLASH's internal address to write to.
  * @param NumByteToWrite : number of bytes to write to the FLASH,
  *                       must be equal or less than "SPI_FLASH_PageSize" value.
  * @retval : None
  */
uint8_t W25Q32_Write ( uint32_t address, uint8_t* buffer,  uint32_t length )
{
    /* Wait the end of Flash writing */
    if( W25Q32_ReadStatus() & JEDEC_STATUS_BUSY ) //busy status
		{
			return 0xff;
		}
	
		uint8_t ret = 0;
    W25Q32_WriteEnable();
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);
    /* Send "Write to Memory " instruction */
    W25Q32_SendReadByte ( JEDEC_PAGE_WRITE );
    /* Send WriteAddr high nibble address byte to write to */
    W25Q32_SendReadByte ( ( address & 0xFF0000 ) >> 16 );
    /* Send WriteAddr medium nibble address byte to write to */
    W25Q32_SendReadByte ( ( address & 0xFF00 ) >> 8 );
    /* Send WriteAddr low nibble address byte to write to */
    W25Q32_SendReadByte ( address & 0xFF );

    /* while there is data to be written on the FLASH */
		ret = W25Q32_SendBuffer(buffer,length);

    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);	
		return ret;
}

/**
  * @brief  Reads a block of data from the FLASH.
  * @param buffer : pointer to the buffer that receives the data read
  *                  from the FLASH.
  * @param address : FLASH's internal address to read from.
  * @param lenght : number of bytes to read from the FLASH.
  * @retval : None
  */
uint8_t W25Q32_Read ( uint32_t address, uint8_t* buffer,  uint32_t length )
{
		uint8_t ret = 0;
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);

    /* Send "Read from Memory " instruction */
    W25Q32_SendReadByte ( JEDEC_READ_DATA );

    /* Send ReadAddr high nibble address byte to read from */
    W25Q32_SendReadByte ( ( address & 0xFF0000 ) >> 16 );
    /* Send ReadAddr medium nibble address byte to read from */
    W25Q32_SendReadByte ( ( address & 0xFF00 ) >> 8);
    /* Send ReadAddr low nibble address byte to read from */
    W25Q32_SendReadByte ( address & 0xFF );

		ret = W25Q32_RecvBuffer(buffer,length);

    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);
	
		return ret;
}
//Reads FLASH identification.
uint8_t W25Q32_ReadID ( jedec_id_t *id )
{
		uint8_t id_buf[3];
		
    /* Select the FLASH: Chip Select low */
    W25Q32_CS(1);

    /* Send "RDID " instruction */
    W25Q32_SendReadByte ( JEDEC_DEVICE_ID );

    /* Read a byte from the FLASH */
    id_buf[0] = W25Q32_SendReadByte ( DUMMY_BYTE );

    /* Read a byte from the FLASH */
    id_buf[1] = W25Q32_SendReadByte ( DUMMY_BYTE );

    /* Read a byte from the FLASH */
    id_buf[2] = W25Q32_SendReadByte ( DUMMY_BYTE );

    /* Deselect the FLASH: Chip Select high */
    W25Q32_CS(0);
		
		id->Manufacturer = (uint16_t)id_buf[1]; 
		id->id = ( (uint16_t)id_buf[1] ) << 8 | id_buf[2];
		
    return 0;
}

static uint8_t W25Q32_WaitForEnd ( uint32_t timeout )
{
    uint8_t FLASH_Status = 0;
		uint8_t time = 0;
	
		uint32_t start_tick = HAL_GetTick();
    /* Loop as long as the memory is busy with a write cycle */
    do
    {
        /* Select the FLASH: Chip Select low */
        W25Q32_CS(1);
        /* Send "Read Status Register" instruction */
        W25Q32_SendReadByte ( JEDEC_READ_STATUS );
        /* Send a dummy byte to generate the clock needed by the FLASH
        and put the value of the status register in FLASH_Status variable */
        FLASH_Status = W25Q32_SendReadByte ( DUMMY_BYTE );
        /* Deselect the FLASH: Chip Select high */
        W25Q32_CS(0);
				
				if( timeout != 0 )
				{
					if( HAL_GetTick() - start_tick >= timeout )
					{
						return 0xff;
					}
				}
    }
    while ( FLASH_Status & JEDEC_STATUS_BUSY );
		
		return 0;
}


//return 1 is busy
uint8_t W25Q32_ReadStatus(void)
{
  uint8_t ret = 0;
  W25Q32_CS(1);
  /* Send "Read Status Register" instruction */
  W25Q32_SendReadByte ( JEDEC_READ_STATUS );
  /* Send a dummy byte to generate the clock needed by the FLASH
  and put the value of the status register in FLASH_Status variable */
  ret = W25Q32_SendReadByte ( DUMMY_BYTE );
  /* Deselect the FLASH: Chip Select high */
  W25Q32_CS(0); 
  return ret & JEDEC_STATUS_BUSY;
}


